﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using ROrganizer.Client;
using ROrganizer.UI.Controllers.Core;

namespace ROrganizer.UI.Controllers
{
    internal class CommitFormController
    {
        private readonly FastObservableCollection<Change> changes;
        private readonly AsyncWorker worker;
        private HashCredentials credentials;

        public CommitFormController(Action<Exception> exceptioner = null)
        {
            this.changes = new FastObservableCollection<Change>();
            this.worker = new AsyncWorker(this.OnChanged, exceptioner: exceptioner);
            this.Refresh();
        }

        public int Progress { get; private set; }

        public event EventHandler Changed;
        private void OnChanged()
        {
            if (this.Changed != null)
                this.Changed(this, EventArgs.Empty);
        }

        public FastObservableCollection<Change> Changes
        {
            get { return this.changes; }
        }

        public bool IsWorking
        {
            get { return this.worker.IsWorking; }
        }

        public bool CanCancel()
        {
            return this.worker.CanStop;
        }

        public void Cancel()
        {
            this.worker.Stop();
        }

        public bool CanCommit(Change[] changes = null)
        {
            return !this.worker.IsWorking
                && (changes ?? this.changes.ToArray()).Any(x => x.Chosen);
        }

        public void Commit(IWin32Window owner, Change[] changes = null)
        {
            if (this.credentials == null)
                this.credentials = ViewManager.ShowCredentials(owner);

            if (this.credentials != null)
            {
                var query = from change in changes ?? this.changes.ToArray()
                            where change.Chosen
                            select change;

                if (query.Any())
                {
                    this.worker.Execute(this.OnCommitCallback, query.ToArray());
                }
            }
        }

        private void OnCommitCallback(Change[] changes)
        {
            var repository = new HashRepository();
            var gateway = new HashGateway();
            var processed = 0;

            foreach (var change in changes)
            {
                if (gateway.Commit(this.credentials, change.Entry))
                {
                    repository.SaveOrUpdate(change.Entry);
                    this.changes.Remove(change);
                }
                else
                {
                    this.credentials = null;
                    break;
                }

                this.Progress = 100 * ++processed / changes.Length;
                this.OnChanged();
            }
        }

        private void Refresh()
        {
            var hrepository = new HashRepository();
            var irepository = new ImdbRepository();

            var query = from hentry in hrepository.All().Where(x => x.IsChanged)
                        join ientry in irepository.All() on hentry.File equals ientry.File
                        select new Change
                        {
                            Title = ientry.Info.Title,
                            Hash = hentry.Info.Hash,
                            Chosen = true,
                            Entry = hentry
                        };

            this.changes.Clear();
            this.changes.AddRange(query);
        }

        [DebuggerDisplay("{Title}")]
        public class Change
        {
            public string Title { get; set; }
            public string Hash { get; set; }
            public bool Chosen { get; set; }
            public HashEntry Entry { get; set; }
        }
    }
}
